#include <AReVi/arClass.h>

#include <AReVi/arSystem.h>
#include <AReVi/activity.h>
#include <AReVi/Lib3D/boundingBox3D.h>
#include <AReVi/Lib3D/viewer3D.h>
#include <AReVi/Lib3D/simpleInteractor.h>
#include <AReVi/Shapes/box3D.h>
using namespace AReVi;

#include "hLib/hLib.h"
using namespace hLib;

#define NB_MODELS_X 5
#define NB_MODELS_Y 6
#define NB_MODELS (NB_MODELS_X*NB_MODELS_Y)

class TestPerf : public Viewer3D {
public:
  AR_CLASS(TestPerf)
  AR_CONSTRUCTOR(TestPerf)

protected:
  ArRef<SimpleInteractor> interactor;
  StlVector<ArRef<Body> > bodies;
  ArRef<KeyframeAnimation> animation;
  StlVector<double> deltas;

  StlVector<ArRef<URLTexture> > textures;

  ArRef<Object3D> _object;

  virtual bool event(ArRef<Activity> act, double dt);
};


AR_CLASS_DEF(TestPerf,Viewer3D)


TestPerf::TestPerf(ArCW& arCW)
: Viewer3D(arCW), interactor(SimpleInteractor::NEW()) {
  interactor->setRenderer(thisRef());

  textures.push_back(URLTexture::NEW("../skins/BatSugar.png", true, true));
  textures.push_back(URLTexture::NEW("../skins/SugarHulk.png", true, true));
  textures.push_back(URLTexture::NEW("../skins/SugarMan2.png", true, true));
  textures.push_back(URLTexture::NEW("../skins/FlashSugar.png", true, true));
  textures.push_back(URLTexture::NEW("../skins/NoNameSugar.png", true, true));
  
  ArRef<Body> body;
  for (unsigned int i = 0; i < NB_MODELS; i++) {
    LoaderData data = HLibLoader::loadFile("../md5/SugarMan.md5mesh");
    if(data.failed) {
      cerr << "error: Can't load model" << endl;
      continue;
    }
    body = data.body;
    body->accessBodyShape()->setShowSkeleton(false);
    unsigned int index = ArSystem::integerRand(textures.size());
    body->accessBodyShape()->accessSkin(0)->setTexture(textures[index]);
    bodies.push_back(body);
    deltas.push_back(ArSystem::realRand()*0.2-0.1);
  }
  ArRef<BoundingBox3D> bbox = BoundingBox3D::NEW();
  body->accessShape()->readBoundingBox(bbox);
  setPosition(NB_MODELS_X*bbox->getMaxSize(),-2.0*NB_MODELS_Y*bbox->getMaxSize(),bbox->getMaxSize() * 3.0);
  yaw(M_PI_2);
  setFarDistance(5.0*NB_MODELS_Y*bbox->getMaxSize());

  for (unsigned int i = 0; i < NB_MODELS_X; i++) {
    for (unsigned int j = 0; j < NB_MODELS_Y; j++) {
      double bruit_x = (ArSystem::realRand()-0.5)*bbox->getMaxSize()*0.5;
      double bruit_y = (ArSystem::realRand()-0.5)*bbox->getMaxSize()*0.5;
      bodies[NB_MODELS_X*i+j]->setPosition(bbox->getMaxSize()*2.0*i+bruit_x, bbox->getMaxSize()*2.0*j+bruit_y, 0.0);
    }
  }
  
  /** On charge l'animation */
  LoaderData data = HLibLoader::loadFile("../md5/BackFlip.md5anim");
  if(data.failed) {
    cerr << "error: Can't load animation" << endl;
  }
  animation = data.animation;
  
  /** On ajoute une boite comme étalon pour le modèle */
  ArRef<Box3D> box = Box3D::NEW();
  box->setSize(5.0,5.0,5.0);
  ArRef<Shape3D> shape = Shape3D::NEW();
  shape->addRootPart(box);
  _object = Object3D::NEW();
  _object->setShape(shape);
  
  /** On lance l'activité pour l'animation */
  ArRef<Activity> act = Activity::NEW(1.0/30.0);
  act->setBehavior(thisRef(), &TestPerf::event);
}

bool TestPerf::event(ArRef<Activity> act, double /*dt*/)
{
  if(animation != Animation::nullRef()) {
    for (unsigned int i = 0; i < bodies.size(); i++) {
      double time = act->getTime()+deltas[i];
      if (time < 0.0) time += animation->getDuration();
      time = fmod(time,animation->getDuration());
      bodies[i]->applyPose(animation->evaluate(time));
    }
    return true;
  }
  else {
    return false;
  }
}


TestPerf::~TestPerf()
{
}

ArRef<Viewer3D> viewer;

ArRef<Scheduler> simulationInit(void) {

  ArRef<Scheduler> scd = RealTimeScheduler::NEW(1e-3);

  viewer=TestPerf::NEW();
  viewer->setCloseAction(Window3D::CLOSE_LEAVE);

  unsigned int w,h;
  Renderer3D::getScreenSize(w,h);
  viewer->setWindowGeometry(w-800,0,800,800);
  viewer->setMapped(true);

  return scd;
}


int main(int argc, char** argv) {
  ArSystem arevi(argc, argv);

  hLibInit();
  TestPerf::REGISTER_CLASS();

  ArSystem::loadPlugin("Imlib2ImageLoader");
  ArSystem::loadPlugin("MagickImageLoader");

  ArSystem::simulationLoop(&simulationInit);

  return 0;
}
